/*

Copyright 2010 Natan Vivo. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:

   1. Redistributions of source code must retain the above copyright notice, this list of
      conditions and the following disclaimer.

   2. Redistributions in binary form must reproduce the above copyright notice, this list
      of conditions and the following disclaimer in the documentation and/or other materials
      provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/

using System;
using System.Globalization;

namespace NVivo.Data
{
    public static class DBConvert
    {
        #region Internal Converters

        private static T? ToNullableValue<T>(object o, IFormatProvider provider, Converter<object, T> converter)
            where T : struct
        {
            if (o is T)
                return (T) o;

            if (o == null || o == DBNull.Value)
                return null;

            return converter(o);
        }

        private static T ToValue<T>(object o, T nullValue, IFormatProvider provider, Converter<object, T> converter)
            where T : struct
        {
            if (o is T)
                return (T) o;

            if (o == null || o == DBNull.Value)
                return nullValue;

            return converter(o);
        }

        #endregion

        #region Boolean

        public static bool? ToNullableBoolean(object o, IFormatProvider provider)
        {
            return ToNullableValue<bool>(o, provider, Convert.ToBoolean);
        }

        public static bool? ToNullableBoolean(object o)
        {
            return ToNullableValue<bool>(o, CultureInfo.CurrentCulture, Convert.ToBoolean);
        }

        public static bool ToBoolean(object o, bool nullValue, IFormatProvider provider)
        {
            return ToValue<bool>(o, nullValue, provider, Convert.ToBoolean);
        }

        public static bool ToBoolean(object o, bool nullValue)
        {
            return ToValue<bool>(o, nullValue, CultureInfo.CurrentCulture, Convert.ToBoolean);
        }

        public static bool ToBoolean(object o)
        {
            return ToValue<bool>(o, default(bool), CultureInfo.CurrentCulture, Convert.ToBoolean);
        }

        #endregion

        #region Byte

        public static byte? ToNullableByte(object o, IFormatProvider provider)
        {
            return ToNullableValue<byte>(o, provider, Convert.ToByte);
        }

        public static byte? ToNullableByte(object o)
        {
            return ToNullableValue<byte>(o, CultureInfo.CurrentCulture, Convert.ToByte);
        }

        public static byte ToByte(object o, byte nullValue, IFormatProvider provider)
        {
            return ToValue<byte>(o, nullValue, provider, Convert.ToByte);
        }

        public static byte ToByte(object o, byte nullValue)
        {
            return ToValue<byte>(o, nullValue, CultureInfo.CurrentCulture, Convert.ToByte);
        }

        public static byte ToByte(object o)
        {
            return ToValue<byte>(o, default(byte), CultureInfo.CurrentCulture, Convert.ToByte);
        }

        #endregion

        #region SByte

        public static sbyte? ToNullableSByte(object o, IFormatProvider provider)
        {
            return ToNullableValue<sbyte>(o, provider, Convert.ToSByte);
        }

        public static sbyte? ToNullableSByte(object o)
        {
            return ToNullableValue<sbyte>(o, CultureInfo.CurrentCulture, Convert.ToSByte);
        }

        public static sbyte ToSByte(object o, sbyte nullValue, IFormatProvider provider)
        {
            return ToValue<sbyte>(o, nullValue, provider, Convert.ToSByte);
        }

        public static sbyte ToSByte(object o, sbyte nullValue)
        {
            return ToValue<sbyte>(o, nullValue, CultureInfo.CurrentCulture, Convert.ToSByte);
        }

        public static sbyte ToSByte(object o)
        {
            return ToValue<sbyte>(o, default(sbyte), CultureInfo.CurrentCulture, Convert.ToSByte);
        }

        #endregion

        #region Int16

        public static short? ToNullableInt16(object o, IFormatProvider provider)
        {
            return ToNullableValue<short>(o, provider, Convert.ToInt16);
        }

        public static short? ToNullableInt16(object o)
        {
            return ToNullableValue<short>(o, CultureInfo.CurrentCulture, Convert.ToInt16);
        }

        public static short ToInt16(object o, short nullValue, IFormatProvider provider)
        {
            return ToValue<short>(o, nullValue, provider, Convert.ToInt16);
        }

        public static short ToInt16(object o, short nullValue)
        {
            return ToValue<short>(o, nullValue, CultureInfo.CurrentCulture, Convert.ToInt16);
        }

        public static short ToInt16(object o)
        {
            return ToValue<short>(o, default(short), CultureInfo.CurrentCulture, Convert.ToInt16);
        }

        #endregion

        #region UInt16

        public static ushort? ToNullableUInt16(object o, IFormatProvider provider)
        {
            return ToNullableValue<ushort>(o, provider, Convert.ToUInt16);
        }

        public static ushort? ToNullableUInt16(object o)
        {
            return ToNullableValue<ushort>(o, CultureInfo.CurrentCulture, Convert.ToUInt16);
        }

        public static ushort ToUInt16(object o, ushort nullValue, IFormatProvider provider)
        {
            return ToValue<ushort>(o, nullValue, provider, Convert.ToUInt16);
        }

        public static ushort ToUInt16(object o, ushort nullValue)
        {
            return ToValue<ushort>(o, nullValue, CultureInfo.CurrentCulture, Convert.ToUInt16);
        }

        public static ushort ToUInt16(object o)
        {
            return ToValue<ushort>(o, default(ushort), CultureInfo.CurrentCulture, Convert.ToUInt16);
        }

        #endregion

        #region Int32

        public static int? ToNullableInt32(object o, IFormatProvider provider)
        {
            return ToNullableValue<int>(o, provider, Convert.ToInt32);
        }

        public static int? ToNullableInt32(object o)
        {
            return ToNullableValue<int>(o, CultureInfo.CurrentCulture, Convert.ToInt32);
        }

        public static int ToInt32(object o, int nullValue, IFormatProvider provider)
        {
            return ToValue<int>(o, nullValue, provider, Convert.ToInt32);
        }

        public static int ToInt32(object o, int nullValue)
        {
            return ToValue<int>(o, nullValue, CultureInfo.CurrentCulture, Convert.ToInt32);
        }

        public static int ToInt32(object o)
        {
            return ToValue<int>(o, default(int), CultureInfo.CurrentCulture, Convert.ToInt32);
        }

        #endregion

        #region UInt32

        public static uint? ToNullableUInt32(object o, IFormatProvider provider)
        {
            return ToNullableValue<uint>(o, provider, Convert.ToUInt32);
        }

        public static uint? ToNullableUInt32(object o)
        {
            return ToNullableValue<uint>(o, CultureInfo.CurrentCulture, Convert.ToUInt32);
        }

        public static uint ToUInt32(object o, uint nullValue, IFormatProvider provider)
        {
            return ToValue<uint>(o, nullValue, provider, Convert.ToUInt32);
        }

        public static uint ToUInt32(object o, uint nullValue)
        {
            return ToValue<uint>(o, nullValue, CultureInfo.CurrentCulture, Convert.ToUInt32);
        }

        public static uint ToUInt32(object o)
        {
            return ToValue<uint>(o, default(uint), CultureInfo.CurrentCulture, Convert.ToUInt32);
        }

        #endregion

        #region Int64

        public static long? ToNullableInt64(object o, IFormatProvider provider)
        {
            return ToNullableValue<long>(o, provider, Convert.ToInt64);
        }

        public static long? ToNullableInt64(object o)
        {
            return ToNullableValue<long>(o, CultureInfo.CurrentCulture, Convert.ToInt64);
        }

        public static long ToInt64(object o, long nullValue, IFormatProvider provider)
        {
            return ToValue<long>(o, nullValue, provider, Convert.ToInt64);
        }

        public static long ToInt64(object o, long nullValue)
        {
            return ToValue<long>(o, nullValue, CultureInfo.CurrentCulture, Convert.ToInt64);
        }

        public static long ToInt64(object o)
        {
            return ToValue<long>(o, default(long), CultureInfo.CurrentCulture, Convert.ToInt64);
        }

        #endregion

        #region UInt64

        public static ulong? ToNullableUInt64(object o, IFormatProvider provider)
        {
            return ToNullableValue<ulong>(o, provider, Convert.ToUInt64);
        }

        public static ulong? ToNullableUInt64(object o)
        {
            return ToNullableValue<ulong>(o, CultureInfo.CurrentCulture, Convert.ToUInt64);
        }

        public static ulong ToUInt64(object o, ulong nullValue, IFormatProvider provider)
        {
            return ToValue<ulong>(o, nullValue, provider, Convert.ToUInt64);
        }

        public static ulong ToUInt64(object o, ulong nullValue)
        {
            return ToValue<ulong>(o, nullValue, CultureInfo.CurrentCulture, Convert.ToUInt64);
        }

        public static ulong ToUInt64(object o)
        {
            return ToValue<ulong>(o, default(ulong), CultureInfo.CurrentCulture, Convert.ToUInt64);
        }

        #endregion

        #region Decimal

        public static decimal? ToNullableDecimal(object o, IFormatProvider provider)
        {
            return ToNullableValue<decimal>(o, provider, Convert.ToDecimal);
        }

        public static decimal? ToNullableDecimal(object o)
        {
            return ToNullableValue<decimal>(o, CultureInfo.CurrentCulture, Convert.ToDecimal);
        }

        public static decimal ToDecimal(object o, decimal nullValue, IFormatProvider provider)
        {
            return ToValue<decimal>(o, nullValue, provider, Convert.ToDecimal);
        }

        public static decimal ToDecimal(object o, decimal nullValue)
        {
            return ToValue<decimal>(o, nullValue, CultureInfo.CurrentCulture, Convert.ToDecimal);
        }

        public static decimal ToDecimal(object o)
        {
            return ToValue<decimal>(o, default(decimal), CultureInfo.CurrentCulture, Convert.ToDecimal);
        }

        #endregion

        #region Single

        public static float? ToNullableSingle(object o, IFormatProvider provider)
        {
            return ToNullableValue<float>(o, provider, Convert.ToSingle);
        }

        public static float? ToNullableSingle(object o)
        {
            return ToNullableValue<float>(o, CultureInfo.CurrentCulture, Convert.ToSingle);
        }

        public static float ToSingle(object o, float nullValue, IFormatProvider provider)
        {
            return ToValue<float>(o, nullValue, provider, Convert.ToSingle);
        }

        public static float ToSingle(object o, float nullValue)
        {
            return ToValue<float>(o, nullValue, CultureInfo.CurrentCulture, Convert.ToSingle);
        }

        public static float ToSingle(object o)
        {
            return ToValue<float>(o, default(float), CultureInfo.CurrentCulture, Convert.ToSingle);
        }

        #endregion

        #region Double

        public static double? ToNullableDouble(object o, IFormatProvider provider)
        {
            return ToNullableValue<double>(o, provider, Convert.ToDouble);
        }

        public static double? ToNullableDouble(object o)
        {
            return ToNullableValue<double>(o, CultureInfo.CurrentCulture, Convert.ToDouble);
        }

        public static double ToDouble(object o, double nullValue, IFormatProvider provider)
        {
            return ToValue<double>(o, nullValue, provider, Convert.ToDouble);
        }

        public static double ToDouble(object o, double nullValue)
        {
            return ToValue<double>(o, nullValue, CultureInfo.CurrentCulture, Convert.ToDouble);
        }

        public static double ToDouble(object o)
        {
            return ToValue<double>(o, default(double), CultureInfo.CurrentCulture, Convert.ToDouble);
        }

        #endregion

        #region DateTime

        public static DateTime? ToNullableDateTime(object o, IFormatProvider provider)
        {
            return ToNullableValue<DateTime>(o, provider, Convert.ToDateTime);
        }

        public static DateTime? ToNullableDateTime(object o)
        {
            return ToNullableValue<DateTime>(o, CultureInfo.CurrentCulture, Convert.ToDateTime);
        }

        public static DateTime ToDateTime(object o, DateTime nullValue, IFormatProvider provider)
        {
            return ToValue<DateTime>(o, nullValue, provider, Convert.ToDateTime);
        }

        public static DateTime ToDateTime(object o, DateTime nullValue)
        {
            return ToValue<DateTime>(o, nullValue, CultureInfo.CurrentCulture, Convert.ToDateTime);
        }

        public static DateTime ToDateTime(object o)
        {
            return ToValue<DateTime>(o, default(DateTime), CultureInfo.CurrentCulture, Convert.ToDateTime);
        }

        #endregion

        #region Char

        public static char? ToNullableChar(object o, IFormatProvider provider)
        {
            return ToNullableValue<char>(o, provider, Convert.ToChar);
        }

        public static char? ToNullableChar(object o)
        {
            return ToNullableValue<char>(o, CultureInfo.CurrentCulture, Convert.ToChar);
        }

        public static char ToChar(object o, char nullValue, IFormatProvider provider)
        {
            return ToValue<char>(o, nullValue, provider, Convert.ToChar);
        }

        public static char ToChar(object o, char nullValue)
        {
            return ToValue<char>(o, nullValue, CultureInfo.CurrentCulture, Convert.ToChar);
        }

        public static char ToChar(object o)
        {
            return ToValue<char>(o, default(char), CultureInfo.CurrentCulture, Convert.ToChar);
        }

        #endregion

        #region Guid

        public static Guid? ToNullableGuid(object o)
        {
            if (o is Guid)
                return (Guid) o;

            if (o == null || o == DBNull.Value)
                return null;

            byte[] buffer = o as byte[];

            if (buffer != null)
                return new Guid(buffer);

            return new Guid(o.ToString());
        }
        
        public static Guid ToGuid(object o, Guid nullValue)
        {
            if (o is Guid)
                return (Guid) o;

            if (o == null || o == DBNull.Value)
                return nullValue;

            byte[] buffer = o as byte[];

            if (buffer != null)
                return new Guid(buffer);

            return new Guid(o.ToString());
        }

        public static Guid ToGuid(object o)
        {
            return ToGuid(o, Guid.Empty);
        }

        #endregion

        #region String

        public static string ToString(object o)
        {
            if (o == null || o == DBNull.Value)
                return null;
            else
                return o.ToString();
        }

        #endregion

        #region ToDBValue

        public static object ToDBValue<T>(T input)
            where T : class
        {
            if (input == null)
                return DBNull.Value;
            else
                return input;
        }

        public static object ToDBValue<T>(T? input)
            where T : struct
        {
            if (input.HasValue)
                return input.Value;
            else
                return DBNull.Value;
        }

        public static object ToDBValue<T>(T input, T nullValue)
            where T : struct, IComparable<T>
        {
            if (input.CompareTo(nullValue) == 0)
                return DBNull.Value;
            else
                return input;
        }

        public static object ToDBValue(string input, int maxLength, bool nullIfEmpty)
        {
            if (input == null || (nullIfEmpty && input.Length == 0))
                return DBNull.Value;

            if (input.Length > maxLength)
                input = input.Substring(0, maxLength);

            return input;
        }

        public static object ToDBValue(string input, int maxLength)
        {
            return ToDBValue(input, maxLength, false);
        }

        public static object ToDBValue(string input, bool nullIfEmpty)
        {
            return ToDBValue(input, Int32.MaxValue, nullIfEmpty);
        }

        #endregion
    }
}
